import { writeFileSync } from 'node:fs'
import { resolve } from 'node:path'

import type { Target } from '@scalar/snippetz'
import { clients } from '@scalar/snippetz/clients'

/**
 * Generator script to create Java enums from TypeScript clients configuration.
 * This script directly imports the clients array from @scalar/snippetz,
 * ensuring the Java enums stay in perfect sync with the TypeScript source of truth.
 */

/**
 * Configuration for Java packages
 */
interface JavaPackageConfig {
  name: string
  enumsDir: string
}

/**
 * Custom SCREAMING_SNAKE_CASE mappings for better Java naming conventions.
 * Only add entries here when the default conversion doesn't produce professional Java names.
 */
const CUSTOM_JAVA_MAPPINGS: Record<string, string> = {
  // Clients that need better naming than default conversion
  'httpclient': 'HTTP_CLIENT',
  'restsharp': 'REST_SHARP',
  'asynchttp': 'ASYNC_HTTP',
  'nethttp': 'NET_HTTP',
  'okhttp': 'OK_HTTP',
  'ofetch': 'O_FETCH',
  'jquery': 'J_QUERY',
  'nsurlsession': 'NSURL_SESSION',
  'cohttp': 'CO_HTTP',
  'webrequest': 'WEB_REQUEST',
  'restmethod': 'REST_METHOD',
  'httpx_sync': 'HTTPX_SYNC',
  'httpx_async': 'HTTPX_ASYNC',

  // Targets that need better naming than default conversion
  'js': 'JAVASCRIPT',

  // Special cases - Non-standard naming requirements
  'http1.1': 'HTTP_11',
}

/**
 * Obsolete client entries for backward compatibility.
 * Add entries here when client names change to maintain backward compatibility.
 */
const OBSOLETE_CLIENT_ENTRIES: { name: string; description: string; reason: string }[] = []

/**
 * Package configurations
 */
const PACKAGE_CONFIGS: JavaPackageConfig[] = [
  {
    name: 'scalar-core',
    enumsDir: '../../../integrations/java/scalar-core/src/main/java/com/scalar/maven/core/enums',
  },
]

/**
 * Helper function to convert a string to SCREAMING_SNAKE_CASE for Java enum values.
 * Uses the custom mappings defined at the top of the file for better naming conventions.
 */
function toScreamingSnakeCase(str: string): string {
  // Check for custom mapping first
  if (CUSTOM_JAVA_MAPPINGS[str]) {
    return CUSTOM_JAVA_MAPPINGS[str]
  }

  // Default SCREAMING_SNAKE_CASE conversion for other cases
  return str
    .split(/[_.-]/)
    .map((part) => part.toUpperCase())
    .join('_')
}

/**
 * Converts the imported clients array to the expected format for enum generation.
 */
function parseClientsFromImport(): {
  targets: Target[]
  allClients: Array<{ target: string; client: string; title: string }>
} {
  const targets: Target[] = []
  const allClientsMap = new Map<string, { target: string; client: string; title: string }>()

  for (const target of clients) {
    const targetClients: Array<{ target: string; client: string; title: string }> = []

    // Sort clients within each target for consistent order
    const sortedClients = [...target.clients].sort((a, b) => a.client.localeCompare(b.client))

    for (const client of sortedClients) {
      const clientInfo = {
        target: target.key,
        client: client.client,
        title: client.title,
      }
      targetClients.push(clientInfo)
      // Use only client name as key to avoid duplicates across targets
      allClientsMap.set(client.client, clientInfo)
    }

    // Create a new target object with sorted clients
    const sortedTarget = {
      ...target,
      clients: sortedClients,
    }
    targets.push(sortedTarget)
  }

  const allClients = Array.from(allClientsMap.values()).sort((a, b) => a.client.localeCompare(b.client))

  // Sort targets by key for consistent enum order
  targets.sort((a, b) => a.key.localeCompare(b.key))

  return { targets, allClients }
}

/**
 * Generates the ScalarTarget enum content.
 */
function generateScalarTargetEnum(targets: Target[]): string {
  const enumValues = targets
    .map((target, index) => createTargetEnumValue(target, index === targets.length - 1))
    .join('\n\n')
  const header = createAutoGeneratedHeader()

  return `${header}

package com.scalar.maven.core.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * Represents the different targets available in Scalar.
 */
public enum ScalarTarget {
${enumValues}

    private final String value;

    ScalarTarget(String value) {
        this.value = value;
    }

    /**
     * Creates a ScalarTarget from a string value.
     *
     * @param value the string value
     * @return the corresponding ScalarTarget
     * @throws IllegalArgumentException if the value is not recognized
     */
    @JsonCreator
    public static ScalarTarget fromValue(String value) {
        for (ScalarTarget target : values()) {
            if (target.value.equals(value)) {
                return target;
            }
        }
        throw new IllegalArgumentException("Unknown target: " + value);
    }

    /**
     * Gets the string value for JSON serialization.
     *
     * @return the string value
     */
    @JsonValue
    public String getValue() {
        return value;
    }
}`
}

function createTargetEnumValue(target: Target, isLast: boolean): string {
  const screamingSnakeCaseKey = toScreamingSnakeCase(target.key)
  const description = `${target.title}.`
  const comma = isLast ? ';' : ','

  return `    /**
     * ${description}
     */
    ${screamingSnakeCaseKey}("${target.key}")${comma}`
}

/**
 * Generates the ScalarClient enum content.
 */
function generateScalarClientEnum(clients: Array<{ target: string; client: string; title: string }>): string {
  const enumValues = clients
    .map((client, index) =>
      createClientEnumValue(client, index === clients.length - 1 && OBSOLETE_CLIENT_ENTRIES.length === 0),
    )
    .join('\n\n')
  const obsoleteEntries = createObsoleteClientEntries()
  const header = createAutoGeneratedHeader()

  return `${header}

package com.scalar.maven.core.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * Represents the different clients available in Scalar.
 */
public enum ScalarClient {
${enumValues}${obsoleteEntries ? '\n\n' + obsoleteEntries : ''}

    private final String value;

    ScalarClient(String value) {
        this.value = value;
    }

    /**
     * Creates a ScalarClient from a string value.
     *
     * @param value the string value
     * @return the corresponding ScalarClient
     * @throws IllegalArgumentException if the value is not recognized
     */
    @JsonCreator
    public static ScalarClient fromValue(String value) {
        for (ScalarClient client : values()) {
            if (client.value.equals(value)) {
                return client;
            }
        }
        throw new IllegalArgumentException("Unknown client: " + value);
    }

    /**
     * Gets the string value for JSON serialization.
     *
     * @return the string value
     */
    @JsonValue
    public String getValue() {
        return value;
    }
}`
}

function createClientEnumValue(client: { target: string; client: string; title: string }, isLast: boolean): string {
  const screamingSnakeCaseKey = toScreamingSnakeCase(client.client)
  const description = `${client.title} client.`
  const comma = isLast ? ';' : ','

  return `    /**
     * ${description}
     */
    ${screamingSnakeCaseKey}("${client.client}")${comma}`
}

function createAutoGeneratedHeader(): string {
  return `//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Scalar Java enum generator.
//     Source: /packages/snippetz/src/clients.ts
//     Command: pnpm generate:java-enums
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------`
}

/**
 * Creates obsolete client enum entries for backward compatibility.
 */
function createObsoleteClientEntries(): string {
  if (OBSOLETE_CLIENT_ENTRIES.length === 0) {
    return ''
  }

  return OBSOLETE_CLIENT_ENTRIES.map(
    (entry, index) => `    /**
     * ${entry.reason}
     *
     * @deprecated ${entry.reason}
     */
    @Deprecated
    ${entry.name}("${entry.description}")${index === OBSOLETE_CLIENT_ENTRIES.length - 1 ? ';' : ','}`,
  ).join('\n\n')
}

/**
 * Generates enums for a specific Java package
 */
function generateForPackage(
  config: JavaPackageConfig,
  targets: Target[],
  allClients: Array<{ target: string; client: string; title: string }>,
): void {
  // Generate enums
  const scalarTargetEnum = generateScalarTargetEnum(targets)
  const scalarClientEnum = generateScalarClientEnum(allClients)

  // Write files
  const enumsDir = resolve(__dirname, config.enumsDir)

  writeFileSync(resolve(enumsDir, 'ScalarTarget.java'), scalarTargetEnum)
  writeFileSync(resolve(enumsDir, 'ScalarClient.java'), scalarClientEnum)

  console.log(`Successfully generated for ${config.name}`)
}

/**
 * Main function to generate all the Java files for all packages.
 */
function main(): void {
  // Skip generation in CI environments
  if (process.env.CI) {
    console.log('Skipping enum generation in CI environment')
    return
  }

  try {
    // Parse all client information from the imported clients array
    const { targets, allClients } = parseClientsFromImport()

    // Generate for each package
    for (const config of PACKAGE_CONFIGS) {
      generateForPackage(config, targets, allClients)
    }

    // Summary of what was found
    console.log('\nSummary:')
    targets.forEach((target) => {
      console.log(`  ${target.title} (${target.key}): ${target.clients.length} clients`)
    })
  } catch (error) {
    console.error('Error generating Java enums:', error)
    process.exit(1)
  }
}

main()
